import java.lang.annotation.*;
import java.lang.reflect.*;

public class Test {
    public static void main(String[] args) throws Exception {
        // 1、Java的注解本身对代码逻辑没有任何影响。
        // 根据@Retention的配置：
        // SOURCE类型的注解在编译期就被丢掉了；
        // CLASS类型的注解仅保存在class文件中，它们不会被加载进JVM；
        // RUNTIME类型的注解会被加载进JVM，并且在运行期可以被程序读取

        // 2、注解定义后也是一种class，所有的注解都继承自java.lang.annotation.Annotation，因此，读取注解，需要使用反射API。
        // Java提供的使用反射API读取Annotation的方法包括：
        // 判断某个注解是否存在于Class、Field、Method或Constructor：
        // Class.isAnnotationPresent(Class)
        // Field.isAnnotationPresent(Class)
        // Method.isAnnotationPresent(Class)
        // Constructor.isAnnotationPresent(Class)

        // 3、使用反射API读取方法
        // 字段和构造方法的Annotation有两种方法。
        // 第一种是先判断Annotation是否存在，如果存在，就直接读取
        Class<Person> cls = Person.class;
        if (cls.isAnnotationPresent(Report.class)) {
            Report report = cls.getAnnotation(Report.class);
            System.out.println("Report: " + report.value());
        }
        // 第二种方法是直接读取Annotation，如果Annotation不存在，将返回null：
        Class<Person> cls1 = Person.class;
        Report report = cls1.getAnnotation(Report.class);
        if (report != null) {
            System.out.println("Report: " + report.value());
        }
        // 4、读取参数的注解
        // 读取方法参数的Annotation就比较麻烦一点，
        // 因为方法参数本身可以看成一个数组，而每个参数又可以定义多个注解，
        // 所以，一次获取方法参数的所有注解就必须用一个二维数组来表示
        Class<Person> cls2 = Person.class;
        // 获取Method实例:
        Method run_Method = cls2.getMethod("run", int.class);
        // 获取所有参数的Annotation:
        Annotation[][] annos = run_Method.getParameterAnnotations();
        // 第一个参数（索引为0）的所有Annotation:
        for (Annotation an : annos[0]) {
            System.err.println(an);
            if(report instanceof Report) {
                System.out.println("Report: " + report.value());
            }
        }

    }
}

@Target({ ElementType.TYPE, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
@interface Report {
    String value() default "default report";
}

@Report("This is a person class")
class Person {
    public static void run(@Report int num) {
        System.out.println("num is " + num);
    }
}